package easycloud.ecs.provider.api.baidu;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.baidu.inf.iis.bcs.BaiduBCS;
import com.baidu.inf.iis.bcs.auth.BCSCredentials;
import com.baidu.inf.iis.bcs.http.HttpMethodName;
import com.baidu.inf.iis.bcs.model.DownloadObject;
import com.baidu.inf.iis.bcs.model.ObjectListing;
import com.baidu.inf.iis.bcs.model.ObjectMetadata;
import com.baidu.inf.iis.bcs.model.ObjectSummary;
import com.baidu.inf.iis.bcs.policy.Policy;
import com.baidu.inf.iis.bcs.request.GenerateUrlRequest;
import com.baidu.inf.iis.bcs.request.GetObjectRequest;
import com.baidu.inf.iis.bcs.request.ListObjectRequest;
import com.baidu.inf.iis.bcs.request.PutObjectRequest;
import com.baidu.inf.iis.bcs.response.BaiduBCSResponse;

import easycloud.ecs.common.domain.Folder;
import easycloud.ecs.common.domain.Item;
import easycloud.ecs.common.domain.Log.LOG_TYPE;
import easycloud.ecs.common.domain.Provider;
import easycloud.ecs.common.domain.Sync;
import easycloud.ecs.provider.api.IProvider;
import easycloud.ecs.provider.api.ProviderException;
import easycloud.ecs.provider.model.IStorageObject;
import easycloud.ecs.provider.model.IStorageObjectMetadata;
import easycloud.ecs.provider.model.baidu.BaiduStorageObject;
import easycloud.ecs.provider.model.baidu.BaiduStorageObjectMetadata;
import easycloud.ecs.provider.service.SendLogService;

public class BaiduBCSProvider implements IProvider {

	private Provider config;

	private SendLogService sendLogService;

	private static BaiduBCS adapter;

	private boolean isReady = false;

	private static final Log logger = LogFactory.getLog(BaiduBCSProvider.class);

	private static String BUCKET_NAME;

	public BaiduBCSProvider(Provider config) {
		this.config = config;
		BaiduBCSProvider.adapter = new BaiduBCS(new BCSCredentials(
				this.config.getAccessKey(), this.config.getSecretKey()),
				"bcs.duapp.com");
		BaiduBCSProvider.adapter.setDefaultEncoding("UTF-8");
		BaiduBCSProvider.BUCKET_NAME = this.config.getBucketName();
	}

	/**
	 * 
	 */
	@Override
	public boolean delete(Item item) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		boolean result = false;
		try {
			BaiduBCSProvider.adapter.deleteObject(BaiduBCSProvider.BUCKET_NAME,
					item.getPath());
			result = true;
			BaiduBCSProvider.logger.info(getName() + " delete OK: "
					+ item.getPath());
			result = true;
		} catch (Exception e) {
			BaiduBCSProvider.logger.warn(getName() + " delete failed", e);
			throw new ProviderException(getName() + " delete failed", e,
					item.getPath());
		} finally {
			easycloud.ecs.common.domain.Log log = new easycloud.ecs.common.domain.Log();
			log.setLogType(LOG_TYPE.DELETE);
			log.setCreateTime(new Date());
			log.setResult(result);
			log.setProvider(config);
			sendLogService.addLog(log);
		}
		return result;
	}

	@Override
	public URL getAccessURL(Item item) {
		GenerateUrlRequest request = new GenerateUrlRequest(HttpMethodName.GET,
				BaiduBCSProvider.BUCKET_NAME, item.getPath());
		String url = BaiduBCSProvider.adapter.generateUrl(request);
		URL result = null;
		try {
			result = new URL(url);
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * 
	 * @param key
	 * @return
	 * @throws ProviderException
	 */
	@Override
	public IStorageObject getItem(Item item) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		boolean result = false;
		DownloadObject object = null;
		try {
			GetObjectRequest getObjectRequest = new GetObjectRequest(
					BaiduBCSProvider.BUCKET_NAME, item.getPath());
			BaiduBCSResponse<DownloadObject> response = BaiduBCSProvider.adapter
					.getObject(getObjectRequest);
			object = response.getResult();
			BaiduBCSProvider.logger.info(getName() + " getFile OK: "
					+ item.getPath());
			result = true;
		} catch (Exception e) {
			BaiduBCSProvider.logger.warn(getName() + " getFile failed", e);
			throw new ProviderException(getName() + " getFile failed", e,
					item.getPath());
		} finally {
			easycloud.ecs.common.domain.Log log = new easycloud.ecs.common.domain.Log();
			log.setLogType(LOG_TYPE.GET);
			if (object != null) {
				log.setLength(object.getObjectMetadata().getContentLength());
			}
			log.setCreateTime(new Date());
			log.setResult(result);
			log.setProvider(config);
			sendLogService.addLog(log);
		}
		if (object != null) {
			return new BaiduStorageObject(object, item);
		} else {
			return null;
		}
	}

	private String getLogicalPath(ObjectSummary summary, Folder folder) {
		String prefix = getPhysicalPath(folder);
		return StringUtils.replaceOnce(summary.getName(), prefix,
				folder.getName());
	}

	@Override
	public String getName() {
		return config.getName();
	}

	private String getPhysicalPath(Folder folder) {
		for (Sync sync : config.getSyncs()) {
			if (sync.getFolder().equals(folder)) {
				return sync.getPath();
			}
		}
		return "";
	}

	/**
	 * 
	 * @param key
	 * @return
	 * @throws ProviderException
	 */
	@Override
	public IStorageObjectMetadata head(Item item) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		ObjectMetadata result = null;
		try {
			BaiduBCSResponse<ObjectMetadata> response = BaiduBCSProvider.adapter
					.getObjectMetadata(BaiduBCSProvider.BUCKET_NAME,
							item.getPath());
			result = response.getResult();
			BaiduBCSProvider.logger.info(getName() + " head OK: "
					+ item.getPath());
		} catch (Exception e) {
			BaiduBCSProvider.logger.warn(getName() + " head failed", e);
			throw new ProviderException(getName() + " head failed", e,
					item.getPath());
		} finally {
			easycloud.ecs.common.domain.Log log = new easycloud.ecs.common.domain.Log();
			log.setLogType(LOG_TYPE.HEAD);
			log.setCreateTime(new Date());
			log.setProvider(config);
			sendLogService.addLog(log);
		}
		if (result != null) {
			return new BaiduStorageObjectMetadata(result, item);
		} else {
			return null;
		}
	}

	/**
	 * 
	 */
	@Override
	public List<IStorageObjectMetadata> list(Folder folder)
			throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		ObjectListing listing = null;
		try {
			String physicalPath = getPhysicalPath(folder);
			ListObjectRequest request = new ListObjectRequest(
					BaiduBCSProvider.BUCKET_NAME);
			request.setPrefix("/" + physicalPath);
			request.setListModel(1);
			BaiduBCSResponse<ObjectListing> response = BaiduBCSProvider.adapter
					.listObject(request);
			listing = response.getResult();
			BaiduBCSProvider.logger.info(getName() + " list OK: "
					+ folder.getName());
		} catch (Exception e) {
			BaiduBCSProvider.logger.warn(getName() + " list failed", e);
			throw new ProviderException(getName() + " list failed", e,
					folder.getName());
		} finally {
			easycloud.ecs.common.domain.Log log = new easycloud.ecs.common.domain.Log();
			log.setLogType(LOG_TYPE.LIST);
			log.setCreateTime(new Date());
			log.setProvider(config);
			sendLogService.addLog(log);
		}
		if (listing != null) {
			List<IStorageObjectMetadata> result = new ArrayList<IStorageObjectMetadata>();
			List<ObjectSummary> adapterResult = listing.getObjectSummaries();
			for (ObjectSummary summary : adapterResult) {
				Item item = new Item();
				String path = getLogicalPath(summary, folder);
				item.setPath(path);
				item.setFolder(folder);
				result.add(new BaiduStorageObjectMetadata(summary, item));
			}
			return result;
		} else {
			return null;
		}
	}

	/**
	 * 
	 * @param file
	 * @param key
	 * @return
	 * @throws ProviderException
	 */
	@Override
	public IStorageObjectMetadata putItem(Item item, InputStream is, long length)
			throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		boolean result = false;
		ObjectMetadata putResult = null;
		if (length > 0) {
			try {
				ObjectMetadata meta = new ObjectMetadata();
				meta.setContentLength(length);
				PutObjectRequest request = new PutObjectRequest(
						BaiduBCSProvider.BUCKET_NAME, item.getPath(), is, meta);
				ObjectMetadata metadata = new ObjectMetadata();
				request.setMetadata(metadata);
				putResult = BaiduBCSProvider.adapter.putObject(request)
						.getResult();
				BaiduBCSProvider.logger.info(getName() + " putFile OK: "
						+ item.getPath());
				result = true;
			} catch (Exception e) {
				BaiduBCSProvider.logger.warn(getName() + " putFile failed", e);
				throw new ProviderException(getName() + " putFile failed", e,
						item.getPath());
			} finally {
				easycloud.ecs.common.domain.Log log = new easycloud.ecs.common.domain.Log();
				log.setLogType(LOG_TYPE.PUT);
				log.setLength(length);
				log.setCreateTime(new Date());
				log.setResult(result);
				log.setProvider(config);
				sendLogService.addLog(log);
			}
		}
		return new BaiduStorageObjectMetadata(putResult, item);
	}

	/**
	 * 
	 */
	@Override
	public boolean ready() {
		if (isReady) {
			return true;
		}

		BaiduBCSResponse<Policy> response = BaiduBCSProvider.adapter
				.getBucketPolicy(BaiduBCSProvider.BUCKET_NAME);
		isReady = response.getResult() != null;
		return isReady;
	}

	@Override
	public void setSendLogService(SendLogService sendLogService) {
		this.sendLogService = sendLogService;
	}

}
